﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.SessionState;
using BizElements.BusinessLayer;

namespace BizElements.Web
{
    /// <summary>
    /// Specifies the page/request elements to trace.
    /// </summary>
    /// <remarks><see cref="PageTraceScope.Default"/> settings can be configured in an application config file.</remarks>
    public sealed class PageTraceScope
    {
        #region FieldPermissions.

        static readonly Regex stringsWhichDontBeginWithTwoUnderscores = new Regex("^(?!__).*$", RegexOptions.Compiled);

        bool rawUrlLogged;
        Regex formVariablesFilter;
        string[] httpHeaders;
        string[] httpParams;
        TimeSpan durationWarningThreshold = TimeSpan.MaxValue;
        bool sessionIdLogged;

        #endregion

        #region Constructors.

        /// <summary>
        /// Initializes a new instance of <b>PageTraceScope</b> class.
        /// </summary>
        public PageTraceScope()
            : this(/*rawUrlLogged*/ true, /*sessionIdLogged*/ true, stringsWhichDontBeginWithTwoUnderscores, null, null)
        {
        }

        /// <summary>
        /// Initializes a new instance of <b>PageTraceScope</b> class.
        /// </summary>
        /// <param name="rawUrlLogged">Whether the <see cref="HttpRequest.RawUrl"/> property is logged.</param>
        /// <param name="formVariablesFilter">Specifies which form varibles, ie. controls, are logged.</param>
        /// <param name="httpHeaders">HTTP headers which are logged.</param>
        /// <param name="httpParams">HTTP variables which are logged.</param>
        public PageTraceScope(bool rawUrlLogged, Regex formVariablesFilter, string[] httpHeaders, string[] httpParams)
        {
            this.RawUrlLogged = rawUrlLogged;
            this.FormVariablesFilter = formVariablesFilter;
            this.HttpHeaders = httpHeaders;
            this.HttpParams = httpParams;
        }

        /// <summary>
        /// Initializes a new instance of <b>PageTraceScope</b> class.
        /// </summary>
        /// <param name="rawUrlLogged">Whether the <see cref="HttpRequest.RawUrl"/> property is logged.</param>
        /// <param name="sessionIdLogged">Whether the <see cref="HttpSessionState.SessionID"/> property is logged.</param>
        /// <param name="formVariablesFilter">Specifies which form varibles, ie. controls, are logged.</param>
        /// <param name="httpHeaders">HTTP headers which are logged.</param>
        /// <param name="httpParams">HTTP variables which are logged.</param>
        public PageTraceScope(bool rawUrlLogged, bool sessionIdLogged, Regex formVariablesFilter, string[] httpHeaders, string[] httpParams)
        {
            this.RawUrlLogged = rawUrlLogged;
            this.SessionIdLogged = sessionIdLogged;
            this.FormVariablesFilter = formVariablesFilter;
            this.HttpHeaders = httpHeaders;
            this.HttpParams = httpParams;
        }

        #endregion

        #region Properties.

        /// <summary>
        /// Gets or sets whether the <see cref="HttpRequest.RawUrl"/> property is logged.
        /// </summary>
        /// <value><b>true</b> if <see cref="HttpRequest.RawUrl"/> is appended to trace; <b>false</b> otherwise. Default is <b>true</b>.</value>
        public bool RawUrlLogged
        {
            get { return this.rawUrlLogged; }
            set { this.rawUrlLogged = value; }
        }

        /// <summary>
        /// Gets or sets optional regular expression which specifies which form varibles, ie. controls, are logged.
        /// </summary>
        /// <value>Regular expression pattern or <b>null</b> if variables are not traced. Default is 
        /// <b>^(?!__).*$</b> which means that all variables except those prefixed with two underscore 
        /// characters, ie. ASP.NET reserved variables, are logged.</value>
        public Regex FormVariablesFilter
        {
            get { return this.formVariablesFilter; }
            set { this.formVariablesFilter = value; }
        }

        /// <summary>
        /// Gets or sets HTTP headers which are logged.
        /// </summary>
        /// <value>An array of strings which contains the headers to log. Default is empty array.</value>
        public string[] HttpHeaders
        {
            get { return this.httpHeaders; }
            set { this.httpHeaders = value ?? new string[0]; }
        }

        /// <summary>
        /// Gets or sets HTTP variables which are logged.
        /// </summary>
        /// <value>An array of strings which contains the params to log. Default is empty array.</value>
        public string[] HttpParams
        {
            get { return this.httpParams; }
            set { this.httpParams = value ?? new string[0]; }
        }

        /// <summary>
        /// Gets or sets the critical page execution duration after which a warning is logged.
        /// </summary>
        /// <value>TimeSpan. Default is <see cref="TimeSpan.MaxValue"/> indicating that no warnings are logged.</value>
        public TimeSpan DurationWarningThreshold
        {
            get { return durationWarningThreshold; }
            set { durationWarningThreshold = value; }
        }

        /// <summary>
        /// Gets or sets whether the <see cref="HttpSessionState.SessionID"/> property is logged.
        /// </summary>
        /// <value><b>true</b> if <see cref="HttpSessionState.SessionID"/> is appended to trace; <b>false</b> otherwise. Default is <b>true</b>.</value>
        public bool SessionIdLogged
        {
            get { return this.sessionIdLogged; }
            set { this.sessionIdLogged = value; }
        }

        #endregion

        #region Default configuration.

        static PageTraceScope defaultConfiguration;

        /// <summary>
        /// Gets a clone of default PageTraceScope as configured in an application's config file.
        /// </summary>
        /// <value>An instance (singleton) of PageTraceScope class.</value>
        /// <remarks>Default configuration is configured in the <b>appSettings</b> section of config file. Example:
        /// <code>
        /// <add key="BizElements.Web.PageTraceScope.RawUrlLogged" value="true"/>
        /// <add key="BizElements.Web.PageTraceScope.SessionIdLogged" value="true"/>
        /// <add key="BizElements.Web.PageTraceScope.FormVariablesFilter" value="^(?!__).*$"/>
        /// <add key="BizElements.Web.PageTraceScope.HttpHeaders" value=""/>
        /// <add key="BizElements.Web.PageTraceScope.HttpParams" value="HTTP_ACCEPT_LANGUAGE;HTTP_USER_AGENT;REMOTE_ADDR"/>
        /// </code>
        /// </remarks>
        public static PageTraceScope Default
        {
            get
            {
                if (defaultConfiguration == null)
                {
                    defaultConfiguration = new PageTraceScope();
                    string cfgRawUrlLogged = ConfigurationManager.AppSettings["BizElements.Web.PageTraceScope.RawUrlLogged"];
                    if (!string.IsNullOrEmpty(cfgRawUrlLogged))
                    {
                        cfgRawUrlLogged = cfgRawUrlLogged.ToLower();
                        defaultConfiguration.RawUrlLogged = (cfgRawUrlLogged == "true") ? true : false;
                    }

                    string cfgFormVariablesFilter = ConfigurationManager.AppSettings["BizElements.Web.PageTraceScope.FormVariablesFilter"];
                    if (cfgFormVariablesFilter != null)
                        defaultConfiguration.FormVariablesFilter = new Regex(cfgFormVariablesFilter, RegexOptions.Compiled);

                    string cfgHttpHeaders = ConfigurationManager.AppSettings["BizElements.Web.PageTraceScope.HttpHeaders"];
                    if (cfgHttpHeaders != null)
                        defaultConfiguration.HttpHeaders = cfgHttpHeaders.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);

                    string cfgHttpParams = ConfigurationManager.AppSettings["BizElements.Web.PageTraceScope.HttpParams"];
                    if (cfgHttpParams != null)
                        defaultConfiguration.HttpParams = cfgHttpParams.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);

                    string cfgDurationWarningThreshold = ConfigurationManager.AppSettings["BizElements.Web.PageTraceScope.DurationWarningThreshold"];
                    if (cfgDurationWarningThreshold != null)
                        defaultConfiguration.DurationWarningThreshold = TimeSpan.Parse(cfgDurationWarningThreshold);

                    string cfgSessionIdLogged = ConfigurationManager.AppSettings["BizElements.Web.PageTraceScope.SessionIdLogged"];
                    if (!string.IsNullOrEmpty(cfgSessionIdLogged))
                    {
                        cfgSessionIdLogged = cfgSessionIdLogged.ToLower();
                        defaultConfiguration.SessionIdLogged = (cfgSessionIdLogged == "true") ? true : false;
                    }
                }

                return (PageTraceScope)defaultConfiguration.MemberwiseClone();
            }
        }

        #endregion
    }
}